#include "lib_standard.fxh"

struct tProcPrama_Noise {

	float amplitude;
	float frequency;
	float octaves;
	float threshold;
} 
noiseParam_lv_color,
noiseParam_lv_normal,
noiseParam_lv_specular,
noiseParam_lv_position,
noiseParam_lv_scale,
noiseParam_lv_rotation;

struct tProcPrama_Checker {

	float contrast;
	float4 color_a;
	float4 color_b;
} 
checkerParam_lv_color,
checkerParam_lv_normal,
checkerParam_lv_specular,
checkerParam_lv_position,
checkerParam_lv_scale,
checkerParam_lv_rotation;

struct tProcPrama_Wood {

	float age_count;
	float age_spacing;
	float4 color_a;
	float4 color_b;
}
woodParam_lv_color,
woodParam_lv_normal,
woodParam_lv_specular,
woodParam_lv_position,
woodParam_lv_scale,
woodParam_lv_rotation;

struct tProcPrama_Grid {

	float amplitude;
	float height;
	float width;
	float4 color_a;
	float4 color_b;
}
gridParam_lv_color,
gridParam_lv_normal,
gridParam_lv_specular,
gridParam_lv_position,
gridParam_lv_scale,
gridParam_lv_rotation;

struct tProcPrama_Ramp {

	float wave;
}
rampParam_lv_color,
rampParam_lv_normal,
rampParam_lv_specular,
rampParam_lv_position,
rampParam_lv_scale,
rampParam_lv_rotation;

//Checker
float3 Procedural_Checker(float3 uvw, tProcPrama_Checker param) {

	uvw = frac(uvw);
	float x = (uvw.x < 0.5f) ? 0.0f : 1.0f;
	float y = (uvw.y < 0.5f) ? 0.0f : 1.0f;
	float z = (uvw.z < 0.5f) ? 0.0f : 1.0f;

	float halfContrast = param.contrast * 0.5f;
	float i = 0.5f + ((x + y + z == 1.0f) ? halfContrast : -halfContrast);
	return (float3)lerp(param.color_a, param.color_b, i);
}

//Grid
float3 Procedural_Grid(float3 uvw, tProcPrama_Grid param) {

float GRID_WIDTH = param.width;
float GRID_HEIGHT = param.height;

float3 uvw_frac = frac(uvw);

float i = clamp(	
		((uvw_frac.x < param.width) ? 1 : 0) +
		((uvw_frac.y < param.height) ? 1 : 0), 0, 1);
	
	i = ((i - 0.5f) * param.amplitude) + .5;
	return (float3)lerp(param.color_a, param.color_b, i);
}

//Ramp
float3 Procedural_Ramp(float3 uvw, tProcPrama_Ramp param) {

	return frac(param.wave * sin(uvw.y) + uvw.x);
}

//Wood
float3 Procedural_Wood(float3 uvw, tProcPrama_Wood param) {

	return float3(0.0f,0.0f,0.0f);
}
#define baseDim 8.0f
float2 ClampLookupBoundry(float2 coord) {

	return (round(frac(coord - (0.5f / baseDim)) * baseDim).xy * (1.0f / baseDim));
}

float Curve(float t)
{
	return t * t * (3 - 2 * t);
}
float2 fade(float2 t)
{
	return t * t * (3 - 2 * t);
}

float2 gradSampler(float2 uv, float octave) {
	
	return (float2)tex2Dlod(g_sam3,float4(uv / (16.0f / octave),0,0)) * 2 - 1;
}

//Noise
float3 Procedural_Noise(float3 uvw, tProcPrama_Noise param) {

	float3 finalColor = 0;
	
	for(int i = 0; i < param.octaves; i++) {
	
		float octave = (i + 1);
		float2 uv = uvw.xy * octave * param.frequency;
		float2 P = floor(uv);
		float2 p = uv - P;
		float2 f = fade(p);
				
		float2 g_00 = normalize(gradSampler(P + float2(0,0), octave));
		float2 g_10 = normalize(gradSampler(P - float2(-1,0), octave));
		float2 g_01 = normalize(gradSampler(P - float2(0,1), octave));
		float2 g_11 = normalize(gradSampler(P - float2(-1,1), octave));
			 			 
		float tr = lerp(dot(g_00, float2(p.xy)), dot(g_10, float2(1 - p.x,p.y)), f.x);
		float br = lerp(dot(g_01, float2(p.x, 1 - p.y)), dot(g_11, float2(1 - p.x, 1 - p.y)), f.x);
		float s = lerp(tr,br,1 - f.y);
		finalColor += (1.0f / pow(2.0f, octave)) * s;
    }
    
    finalColor = clamp(param.amplitude * (finalColor + 1) * 0.5, param.threshold, 1);
	
	return finalColor;
}

//Combines a procedural evaluator for a specific channel
void Channel_Combiner(int channel, float3 position_os, float3 normal_ws, float2 uv_ts, out float3 result) {
 
	float denominator = 0.0f;
	result = (float3)0.0f;

	float3 coordinates = 
#ifdef DIMENSIONS_3D
		position_os;
#else
		float3(uv_ts, 0.0f);
#endif
	
	if( false ) {}
	#ifdef CHANNEL_DECAL
	else if (channel == CHANNEL_DECAL)
	{	
		#ifdef SAMPLE_TEXTURE_CHANNEL_DECAL
			result += Texture_Unit0(float3(uv_ts, 0.0f));
			denominator++;
		#endif
		#ifdef SAMPLE_CHECKER_CHANNEL_DECAL
			result += Procedural_Checker(coordinates, checkerParam_lv_color);
			denominator++;
		#endif
		#ifdef SAMPLE_RAMP_CHANNEL_DECAL
			result += Procedural_Ramp(coordinates, rampParam_lv_color);
			denominator++;
		#endif
		#ifdef SAMPLE_GRID_CHANNEL_DECAL
			result += Procedural_Grid(coordinates, gridParam_lv_color);
			denominator++;
		#endif
		#ifdef SAMPLE_WOOD_CHANNEL_DECAL
			result += Procedural_Wood(coordinates, woodParam_lv_color);
			denominator++;
		#endif
		#ifdef SAMPLE_NOISE_CHANNEL_DECAL
			result += Procedural_Noise(coordinates, noiseParam_lv_color);
			denominator++;
		#endif
	}
	#endif
	#ifdef CHANNEL_NORMAL
	else if (channel == CHANNEL_NORMAL)
	{
		#ifdef SAMPLE_TEXTURE_CHANNEL_NORMAL
			result += Texture_Unit1(float3(uv_ts, 0.0f));
			denominator++;
		#endif
		#ifdef SAMPLE_CHECKER_CHANNEL_NORMAL
			result += Procedural_Checker(coordinates, checkerParam_lv_normal);
			denominator++;
		#endif
		#ifdef SAMPLE_RAMP_CHANNEL_NORMAL
			result += Procedural_Ramp(coordinates, rampParam_lv_normal);
			denominator++;
		#endif
		#ifdef SAMPLE_GRID_CHANNEL_NORMAL
			result += Procedural_Grid(coordinates, gridParam_lv_normal);
			denominator++;
		#endif
		#ifdef SAMPLE_WOOD_CHANNEL_NORMAL
			result += Procedural_Wood(coordinates, woodParam_lv_normal);
			denominator++;
		#endif
		#ifdef SAMPLE_NOISE_CHANNEL_NORMAL
			result += Procedural_Noise(coordinates, noiseParam_lv_normal);
			denominator++;
		#endif
	}
	#endif
	#ifdef CHANNEL_SPECULAR
	else if (channel == CHANNEL_SPECULAR)
	{
		#ifdef SAMPLE_TEXTURE_CHANNEL_SPECULAR
			result += Texture_Unit2(float3(uv_ts, 0.0f));
			denominator++;
		#endif
		#ifdef SAMPLE_CHECKER_CHANNEL_SPECULAR
			result += Procedural_Checker(coordinates, checkerParam_lv_specular);
			denominator++;
		#endif
		#ifdef SAMPLE_RAMP_CHANNEL_SPECULAR
			result += Procedural_Ramp(coordinates, rampParam_lv_specular);
			denominator++;
		#endif
		#ifdef SAMPLE_GRID_CHANNEL_SPECULAR
			result += Procedural_Grid(coordinates, gridParam_lv_specular);
			denominator++;
		#endif
		#ifdef SAMPLE_WOOD_CHANNEL_SPECULAR
			result += Procedural_Wood(coordinates, woodParam_lv_specular);
			denominator++;
		#endif
		#ifdef SAMPLE_NOISE_CHANNEL_SPECULAR
			result += Procedural_Noise(coordinates, noiseParam_lv_specular);
			denominator++;
		#endif
	}
	#endif
	else 
	{
		result = (float3)0.0f;
		result.x = 1;
		denominator = 1.0f;
	}

	denominator = 1.0f / denominator;
	result = isnan(denominator) ? (float3)0.0f : (denominator * result);
}

void Decal_Combiner(float3 position_os, float3 normal_ws, float2 uv_ts, out float3 result) {

#ifdef CHANNEL_DECAL
	Channel_Combiner(CHANNEL_DECAL, position_os, normal_ws, uv_ts, result);
#else
	result = float3(1.0f,1.0f,1.0f);
#endif
 
}
 
void Normal_Combiner(float3 position_os, float3 normal_ws, float2 uv_ts, out float3 result) {

#ifdef CHANNEL_NORMAL
	Channel_Combiner(CHANNEL_NORMAL, position_os, normal_ws, uv_ts, result);
#else
	result = float3(0.5f,0.5f,0.5f);
#endif
 
}
 
void Specular_Combiner(float3 position_os, float3 normal_ws, float2 uv_ts, out float3 result) {

#ifdef CHANNEL_SPECULAR
	Channel_Combiner(CHANNEL_SPECULAR, position_os, normal_ws, uv_ts, result);
#else
	result = float3(0.5f,0.5f,0.5f);
#endif
 
}